/*
* #%L
* wcm.io
* %%
* Copyright (C) 2014 wcm.io
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package io.wcm.sling.models.injectors.impl;
import io.wcm.sling.models.annotations.AemObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.spi.AcceptsNullName;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
import org.apache.sling.models.spi.Injector;
import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
import org.osgi.framework.Constants;
import com.adobe.granite.xss.XSSAPI;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.WCMMode;
import com.day.cq.wcm.api.components.ComponentContext;
import com.day.cq.wcm.api.designer.Design;
import com.day.cq.wcm.api.designer.Designer;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.commons.WCMUtils;
/**
* Injects common AEM objects that can be derived from a SlingHttpServletRequest.
* Documentation see {@link AemObject}.
*/
@Component
@Service
/*
* SERVICE_RANKING of this service should be lower than the ranking of the OsgiServiceInjector (5000),
* otherwise the generic XSSAPI service would be injected from the OSGi Service Registry instead of the
* pre-configured from the current request.
*/
@Property(name = Constants.SERVICE_RANKING, intValue = 4500)
public final class AemObjectInjector implements Injector, InjectAnnotationProcessorFactory, AcceptsNullName {
/**
* Injector name
*/
public static final String NAME = "wcm-io-aem-object";
private static final String RESOURCE_PAGE = "resourcePage";
@Override
public String getName() {
return NAME;
}
@Override
public Object getValue(final Object adaptable, final String name, final Type type, final AnnotatedElement element,
final DisposalCallbackRegistry callbackRegistry) {
// only class types are supported
if (!(type instanceof Class<?>)) {
return null;
}
Class<?> requestedClass = (Class<?>)type;
if (adaptable instanceof SlingHttpServletRequest) {
SlingHttpServletRequest request = (SlingHttpServletRequest)adaptable;
if (requestedClass.equals(WCMMode.class)) {
return getWcmMode(request);
}
if (requestedClass.equals(ComponentContext.class)) {
return getComponentContext(request);
}
if (requestedClass.equals(Style.class)) {
return getStyle(request);
}
if (requestedClass.equals(XSSAPI.class)) {
return getXssApi(request);
}
}
if (requestedClass.equals(PageManager.class)) {
return getPageManager(adaptable);
}
else if (requestedClass.equals(Page.class)) {
if (StringUtils.equals(name, RESOURCE_PAGE)) {
return getResourcePage(adaptable);
}
else {
return getCurrentPage(adaptable);
}
}
else if (requestedClass.equals(Designer.class)) {
return getDesigner(adaptable);
}
else if (requestedClass.equals(Design.class)) {
return getCurrentDesign(adaptable);
}
return null;
}
private ResourceResolver getResourceResolver(final Object adaptable) {
if (adaptable instanceof SlingHttpServletRequest) {
return ((SlingHttpServletRequest)adaptable).getResourceResolver();
}
if (adaptable instanceof ResourceResolver) {
return (ResourceResolver)adaptable;
}
if (adaptable instanceof Resource) {
return ((Resource)adaptable).getResourceResolver();
}
return null;
}
private Resource getResource(final Object adaptable) {
if (adaptable instanceof SlingHttpServletRequest) {
return ((SlingHttpServletRequest)adaptable).getResource();
}
if (adaptable instanceof Resource) {
return (Resource)adaptable;
}
return null;
}
private PageManager getPageManager(final Object adaptable) {
ResourceResolver resolver = getResourceResolver(adaptable);
if (resolver != null) {
return resolver.adaptTo(PageManager.class);
}
return null;
}
private Designer getDesigner(final Object adaptable) {
ResourceResolver resolver = getResourceResolver(adaptable);
if (resolver != null) {
return resolver.adaptTo(Designer.class);
}
return null;
}
private Page getCurrentPage(final Object adaptable) {
if (adaptable instanceof SlingHttpServletRequest) {
ComponentContext context = getComponentContext((SlingHttpServletRequest)adaptable);
if (context != null) {
return context.getPage();
}
}
return getResourcePage(adaptable);
}
private Page getResourcePage(final Object adaptable) {
PageManager pageManager = getPageManager(adaptable);
Resource resource = getResource(adaptable);
if (pageManager != null && resource != null) {
return pageManager.getContainingPage(resource);
}
return null;
}
private WCMMode getWcmMode(final SlingHttpServletRequest request) {
return WCMMode.fromRequest(request);
}
private ComponentContext getComponentContext(final SlingHttpServletRequest request) {
return WCMUtils.getComponentContext(request);
}
private Design getCurrentDesign(final Object adaptable) {
Page currentPage = getCurrentPage(adaptable);
Designer designer = getDesigner(adaptable);
if (currentPage != null && designer != null) {
return designer.getDesign(currentPage);
}
return null;
}
private Style getStyle(final SlingHttpServletRequest request) {
Design currentDesign = getCurrentDesign(request);
ComponentContext componentContext = getComponentContext(request);
if (currentDesign != null && componentContext != null) {
return currentDesign.getStyle(componentContext.getCell());
}
return null;
}
private XSSAPI getXssApi(final SlingHttpServletRequest request) {
return request.adaptTo(XSSAPI.class);
}
@Override
public InjectAnnotationProcessor createAnnotationProcessor(final Object adaptable, final AnnotatedElement element) {
// check if the element has the expected annotation
AemObject annotation = element.getAnnotation(AemObject.class);
if (annotation != null) {
return new AemObjectAnnotationProcessor(annotation);
}
return null;
}
private static class AemObjectAnnotationProcessor extends AbstractInjectAnnotationProcessor {
private final AemObject annotation;
public AemObjectAnnotationProcessor(final AemObject annotation) {
this.annotation = annotation;
}
@Override
public Boolean isOptional() {
return this.annotation.optional();
}
}
}